﻿#region using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using iTextSharp.text.pdf;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.X509;
using iTextSharp.text;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Security;
using iTextSharp.text.pdf.security; 
#endregion

namespace ItsPdfUtils
{
	/// <summary>
	/// 
	/// </summary>
	public static class SignatureUtils
	{
		#region public static void Sign(string keystore, int level, string src, string dest, string name, string reason = null, string location = null)
		/// <summary>
		/// 
		/// </summary>
		/// <param name="keystore"></param>
		/// <param name="level"></param>
		/// <param name="src"></param>
		/// <param name="dest"></param>
		/// <param name="name"></param>
		/// <param name="reason"></param>
		/// <param name="location"></param>
		public static void Sign(CertificateItem cert, int level, string src, string dest, string name, string reason = null, string location = null)
		{
			if (cert == null)
			{
				throw new InvalidOperationException("Please select certificate!");
			}



			//using (var fks = File.OpenRead(keystore))
			using (var reader = new PdfReader(src))
			using (var os = File.OpenWrite(dest))
			using (var stamper = PdfStamper.CreateSignature(reader, os, '\0', null, true))
			{
				//set initial variables 
				//var chain = X509Chain.Create();
				//var sap = stamper.SignatureAppearance;
				//var signatureDictionary = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
				//var reservedSpace = new Dictionary<PdfName, int>();
				//var sha = new SHA1CryptoServiceProvider();
				//int read = 0;
				//byte[] buff = new byte[8192];
				//int csize = 0;
				//Stream sapStream = null;
				//byte[] pk = null;
				//var certificateDictionary = new PdfDictionary();

				////build my certificate chain 
				//chain.Build(cert.Certificate);

				////guess the size of the certificates and signature. 
				////we don't know for sure how big it really is going to be until after we hash it, so, we give it 4k for the signature 
				////and add the size of all the raw data of the certificates. 
				//csize = 4096 +  //4K for the signature 
				//    (from c in chain.ChainElements.OfType<X509ChainElement>()
				//     select c.Certificate.RawData.Length).Sum(); //add up the size of each of the certificates and add it up 

				////certificate buffer 
				//var certBuffer = new byte[csize];

				////protect certain features of the document 
				////stamper.SetEncryption(null,
				////    Guid.NewGuid().ToByteArray(), //random password 
				////    PdfWriter.ALLOW_PRINTING |
				////    PdfWriter.ALLOW_COPY |
				////    PdfWriter.ALLOW_SCREENREADERS,
				////    PdfWriter.ENCRYPTION_AES_256);


				////////sign the document 
				//sap.SetVisibleSignature(new Rectangle(100, 100, 100, 100), 1, "Signature field");
				//sap.SignDate = DateTime.Parse(cert.Certificate.GetEffectiveDateString());
				//if (reason != null)
				//	sap.Reason = reason;
				//if (location != null)
				//	sap.Location = location;

				////set to use the wincer_signed, its a windows cert, not sure if this is needed. 
				////sap.SetCrypto(null, null, null, PdfSignatureAppearance.WINCER_SIGNED);

				////set some stuff in the signature dictionary. we dont need to set the appearance stuff because the signature is hidden. 
				//signatureDictionary.Reason = sap.Reason;
				//signatureDictionary.Location = sap.Location;
				//signatureDictionary.Date = new PdfDate(sap.SignDate);
				//signatureDictionary.Name = cert.Subject; //certificate name 

				////set the appearance only stuff. 
				//sap.Acro6Layers = true; //set to true to make adobe 6.x and higher happier with the layers. 
				//sap.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription; //what to show in the signature display. since its hidden, we dont care 
				////                                                                        but if we do allow an image, then we have to put in an image. 

				////set the crypto dictionary 
				//sap.CryptoDictionary = signatureDictionary;

				////reserve some space for certs and signatures 
				//reservedSpace[PdfName.CONTENTS] = csize * 2 + 2; //*2 because binary data is stored as hex strings (i think). +2 for end of field 
				//sap.PreClose(reservedSpace); //actually reserve it 

				////build the signature 
				//sapStream = sap.RangeStream;
				//while ((read = sapStream.Read(buff, 0, 8192)) > 0)
				//{
				//	sha.TransformBlock(buff, 0, read, buff, 0);
				//}
				//sha.TransformFinalBlock(buff, 0, 0);
				//pk = SignMsg(sha.Hash, chain, false);

				////put the certs and signature into the reserved buffer 
				//Array.Copy(pk, 0, certBuffer, 0, pk.Length);

				////put the reserved buffer into the reserved space 
				//certificateDictionary.Put(PdfName.CONTENTS, new PdfString(certBuffer).SetHexWriting(true));

				////write the signature 
				//sap.Close(certificateDictionary);

				////close the stamper and save it 
				//stamper.Close();

				////close the reader 
				//reader.Close();

				////return the saved pdf 
				//return stream.GetBuffer();
				////var pk12 = new Pkcs12Store();//fks, "password".ToCharArray());


				var bcert = DotNetUtilities.FromX509Certificate(cert.Certificate);


				var chain = X509Chain.Create();
				chain.Build(cert.Certificate);

				var bchainList = new List<Org.BouncyCastle.X509.X509Certificate>();
				foreach (var cc in chain.ChainElements)
				{
					bchainList.Add(DotNetUtilities.FromX509Certificate(cc.Certificate));
				}
			

				//var bchain = bchainList.ToArray();


				var sap = stamper.SignatureAppearance;
				//sap.Certificate = akp;//.SetCrypto(akp, chain, null, PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
				if (reason != null)
					sap.Reason = reason;
				if (location != null)
					sap.Location = location;

				sap.SetVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);

				var pks = new X509Certificate2Signature(cert.Certificate, "SHA-1");
				MakeSignature.SignDetached(sap, pks, bchainList.ToArray(), null, null, null, 0, CryptoStandard.CMS);
				//stamper.Close();
			}
		}
		#endregion

		#region public static X509Store GetStore()
		/// <summary>
		/// Gets a store of certificates in Windows
		/// </summary>
		/// <returns></returns>
		public static X509Store GetStore()
		{
			X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
			store.Open(OpenFlags.MaxAllowed);
			return store;
		}
		#endregion

		#region public static X509Certificate2 GetCertificateByThumbprint(string thumbprint, bool requirePrivate)
		/// <summary>
		/// 
		/// </summary>
		/// <param name="thumbprint"></param>
		/// <param name="requirePrivate"></param>
		/// <returns></returns>
		public static X509Certificate2 GetCertificateByThumbprint(string thumbprint, bool requirePrivate)
		{
			if (string.IsNullOrEmpty(thumbprint))
			{
				return null;
			}

			X509Store store = GetStore();
			X509Certificate2 result = null;
			try
			{
				foreach (X509Certificate2 c in store.Certificates)
				{
					if (c.Thumbprint == thumbprint &&
					    (c.HasPrivateKey || !requirePrivate))
						result = c;
				}
			}
			catch
			{
			}
			finally
			{
				store.Close();
			}

			return result;
		}
		#endregion

		#region public static CertificateItems GetAvailableCertificates()
		/// <summary>
		/// returns all available certificates from local user store
		/// </summary>
		/// <returns></returns>
		public static CertificateItems GetAvailableCertificates()
		{
			X509Store store = GetStore();
			var ret = new CertificateItems();
			foreach (X509Certificate2 c in store.Certificates)
			{
				if (c.HasPrivateKey && c.NotAfter > DateTime.UtcNow)
					ret.Add(new CertificateItem(c));
			}
			return ret;
		}
		#endregion

		//public static byte[] SignMsg(Byte[] msg, X509Chain chain, bool detached)
		//{
		//	//  Place message in a ContentInfo object. 
		//	//  This is required to build a SignedCms object. 
		//	ContentInfo contentInfo = new ContentInfo(msg);

		//	//  Instantiate SignedCms object with the ContentInfo above. 
		//	//  Has default SubjectIdentifierType IssuerAndSerialNumber. 
		//	SignedCms signedCms = new SignedCms(contentInfo, detached);

		//	//  Formulate a CmsSigner object for the signer. 
		//	CmsSigner cmsSigner = new CmsSigner(chain.ChainElements[0].Certificate); //first cert in the chain is the signer cert 

		//	//  Do the whole certificate chain. This way intermediate certificates get sent across as well. 
		//	//cmsSigner.IncludeOption = X509IncludeOption.ExcludeRoot;

		//	//  Sign the CMS/PKCS #7 message. The second argument is 
		//	//  needed to ask for the pin. 
		//	signedCms.ComputeSignature(cmsSigner, false);

		//	//  Encode the CMS/PKCS #7 message. 
		//	return signedCms.Encode();
		//}
	}
}
